package com.xbongbong.pro.statistics.strategy.chart.type.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.paas.constant.FieldAttrConstant;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.constant.XbbConstant;
import com.xbongbong.paas.enums.FieldTypeEnum;
import com.xbongbong.paas.redis.PaasRedisHelper;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.helper.PageHelper;
import com.xbongbong.paas.toolbox.util.CollectionsUtil;
import com.xbongbong.paas.toolbox.util.StringUtil;
import com.xbongbong.pro.bi.commons.pojo.vo.GroupSqlVO;
import com.xbongbong.pro.domain.entity.ChartEntity;
import com.xbongbong.pro.enums.TimeTypeEnum;
import com.xbongbong.pro.enums.errorcodes.ChartErrorCodeEnum;
import com.xbongbong.pro.enums.TimeRangeOperationEnum;
import com.xbongbong.pro.statistic.enums.AdvancedComputingFirstEnum;
import com.xbongbong.pro.statistic.enums.ChartTypeEnum;
import com.xbongbong.pro.statistic.enums.NormalTypeEnum;
import com.xbongbong.pro.statistic.help.ChartResultHelp;
import com.xbongbong.pro.statistic.pojo.ChartFieldAttrPojo;
import com.xbongbong.pro.statistic.pojo.FiscalYearTimePojo;
import com.xbongbong.pro.statistic.result.pojo.ChartResultPojo;
import com.xbongbong.pro.statistic.result.pojo.FieldAttrPojo;
import com.xbongbong.pro.statistic.result.pojo.vo.ChartAnalysisVO;
import com.xbongbong.pro.statistics.help.AggregationlHelper;
import com.xbongbong.pro.statistics.help.CkResultHelp;
import com.xbongbong.pro.statistics.help.CommonHelper;
import com.xbongbong.pro.statistics.help.DisplayHelper;
import com.xbongbong.pro.statistics.help.InitSqlHelper;
import com.xbongbong.pro.statistics.help.TargetChartVOHelper;
import com.xbongbong.pro.statistics.pojo.dto.ChartAnalysisDTO;
import com.xbongbong.pro.statistics.pojo.dto.GenerateSqlDTO;
import com.xbongbong.pro.statistics.pojo.vo.GenerateSqlVO;
import com.xbongbong.pro.statistics.strategy.chart.type.ChartTypeStrategyService;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.StringConstant;
import com.xbongbong.saas.service.toolbox.help.AttrDecimalPrecisionHelper;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import static com.xbongbong.pro.statistics.help.InitSqlHelper.getIsMaxPermission;


/**
 * 指标图
 *
 * @author xinpeng.jiang
 * @version V1.0
 * @date 2021/5/17 10:00
 * @since V1.0
 */
@Service
public class TargetChartStrategyServiceImpl implements ChartTypeStrategyService {

    @Resource
    private CkResultHelp ckResultHelp;
    @Resource
    private AttrDecimalPrecisionHelper attrDecimalPrecisionHelper;
    @Resource
    private ChartResultHelp chartResultHelp;
    @Resource
    private InitSqlHelper initSqlHelper;
    @Resource
    private PaasRedisHelper paasRedisHelper;
    @Resource
    private AggregationlHelper aggregationlHelper;
    @Resource
    private CommonHelper commonHelper;
    @Resource
    private DisplayHelper displayHelper;
    @Resource
    private TargetChartVOHelper targetChartVOHelper;

    public Logger logger = LoggerFactory.getLogger(getClass());

    @Override
    public ChartAnalysisVO chartAnalysis(ChartAnalysisDTO chartAnalysisDTO) throws XbbException {
        Map<String, Map<String, String>> attrLinkMap = aggregationlHelper.getLinkMap(chartAnalysisDTO.getChartEntity(),  chartAnalysisDTO.getResultArray());
        return targetChartVOHelper.targetChartAnalysis(chartAnalysisDTO,attrLinkMap);
    }



    @Override
    public GenerateSqlVO generateSql(GenerateSqlDTO generateSqlDTO) throws XbbException {
        GenerateSqlVO generateSqlVO = new GenerateSqlVO();
        ChartEntity chartEntity = generateSqlDTO.getChartEntity();
        if (chartEntity.getSummaryAttr().size() <= 0 ) {
            return generateSqlVO;
        }
        ChartResultPojo chartResultPojo = generateSqlDTO.getChartResultPojo();
        boolean isSingle = generateSqlDTO.getSingle();
        StringBuffer stringBuffer = new StringBuffer();
        Map<Long, String> formIdTableMap = commonHelper.getFormIdTableMap(chartEntity, isSingle);
        List<ChartFieldAttrPojo> groupAttrList = JSONArray.parseArray(chartEntity.getRowGroupAttr().toJSONString(), ChartFieldAttrPojo.class);
        List<FieldAttrPojo> summaryAttrList = JSONArray.parseArray(chartEntity.getSummaryAttr().toJSONString(), FieldAttrPojo.class);
        // 表头处理，渲染时用
        JSONArray tableHead = generateSqlDTO.getTableHead();
        Map<JSONObject, FieldAttrPojo> parseAttrMap = generateSqlDTO.getParseAttrMap();
        Map<String, FieldAttrPojo> summaryAttrMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        handleHeadName4Target(chartEntity, isSingle, formIdTableMap, groupAttrList, tableHead, parseAttrMap);
        displayHelper.handleSummary(summaryAttrList, tableHead, parseAttrMap, summaryAttrMap, isSingle);
        
        // 副指标
        List<FieldAttrPojo> secondTargetList = null;
        if(Objects.nonNull(chartEntity.getSecondTargetAttr())){
            secondTargetList = JSONArray.parseArray(chartEntity.getSecondTargetAttr().toJSONString(), FieldAttrPojo.class);
        }
        List<String> secondTargetKey = new ArrayList<>();
        if (Objects.equals(chartEntity.getChartType(), ChartTypeEnum.SECOND_TARGET_CHART.getCode())){
            if (CollectionUtils.isEmpty(secondTargetList)){
                throw new XbbException(ChartErrorCodeEnum.API_ERROR_310001, ChartErrorCodeEnum.API_ERROR_310001.getMsg());
            }
            FieldAttrPojo fieldAttrPojo = secondTargetList.get(0);
            JSONArray secondAdvancedComputing = fieldAttrPojo.getAdvancedComputing();
            if (CollectionsUtil.isNotEmpty(secondAdvancedComputing) && !Objects.equals(secondAdvancedComputing.getString(0), AdvancedComputingFirstEnum.NONE.getAlias())) {
                // 将副指标里面的统计时间封装成分组字段
                ChartFieldAttrPojo timeAttr = JSONObject.parseObject(chartEntity.getTimeAttr().toJSONString(),ChartFieldAttrPojo.class);
                timeAttr.setLabel(timeAttr.getAttrName());
                timeAttr.setLabelName(timeAttr.getAttrName());
                String timeDimension = fieldAttrPojo.getTimeDimension();
                // 如果图表中心此时给的全局或者非全局筛选范围跟设置（timeDimension）的时候不一样，就直接返回图表设计未完成，正常就返回筛选范围
                NormalTypeEnum normalTypeEnum = validSecondAdvancedRule(generateSqlDTO, timeDimension);
                timeAttr.setDateGroupType(timeDimension);
                groupAttrList.add(timeAttr);
                chartEntity.setRowGroupAttr(JSONArray.parseArray(JSON.toJSONString(groupAttrList)));
                //将时间维度封装成筛选条件
                JSONArray filter = chartEntity.getFilter();
                if (Objects.isNull(filter)){
                    filter = new JSONArray();
                }
                List<Long> valueList = chartResultHelp.getSecondTargetFilter(timeDimension, secondAdvancedComputing, secondTargetKey, normalTypeEnum);
                if (CollectionUtils.isNotEmpty(valueList)){
                    JSONObject conditionJson = (JSONObject) JSONObject.toJSON(timeAttr);
                    conditionJson.put("symbol", "range");
                    conditionJson.put("value", valueList);
                    filter.add(conditionJson);
                }
                chartEntity.setFilter(filter);
            }
        }

        //筛选+查询
        Map<String,StringBuffer>  fliterMap = new HashMap<>(XbbConstant.INITIALCAPACITY);
        initSqlHelper.parseConditionIntersection(fliterMap, chartEntity, false);
        Map<String, String> sortMap = displayHelper.getAggSortMap(chartEntity, chartResultPojo.getSortStatisticsMap(),chartResultPojo.getSortFieldOrder());
        //聚合条件 要放置两份，一份在colums,一份在group BY
        Pair<GroupSqlVO, HashMap<String, Set<String>>> groupSqlVOHashMapPair = aggregationlHelper.targetChartAggregation(chartEntity, formIdTableMap, sortMap);
        GroupSqlVO groupSqlVO = groupSqlVOHashMapPair.getKey();
        List<String> groupFieldsList = groupSqlVO.getGroupFieldsList();
        List<String> aggFieldsList = groupSqlVO.getAggFieldsList();
        List<String> groupColumnsList  = groupSqlVO.getGroupColumnsList();
        StringBuilder gropuColumns  = new StringBuilder();
        for (int i = 0; i < groupColumnsList.size(); i++) {
            gropuColumns.append(groupColumnsList.get(i));
            if(i != groupColumnsList.size()-1){
                gropuColumns.append(",");
            }
        }
        StringBuilder gropuFields  = new StringBuilder();
        for (int i = 0; i < groupFieldsList.size(); i++) {
            gropuFields.append(groupFieldsList.get(i));
            if(i != groupFieldsList.size()-1){
                gropuFields.append(",");
            }
        }
        StringBuffer aggFields  = new StringBuffer();
        for (int i = 0; i < aggFieldsList.size(); i++) {
            aggFields.append(aggFieldsList.get(i));
            if(i != aggFieldsList.size()-1){
                aggFields.append(",");
            }
        }
        if(StringUtil.isNotEmpty(gropuColumns.toString())){
            aggFields.append(",").append(gropuColumns);
        }
        initSqlHelper.buildSubColumnOfCount(groupSqlVO,groupColumnsList,Boolean.TRUE);
        String sortField = displayHelper.getAggSortField(sortMap,groupSqlVO);
        boolean isMaxPermission = getIsMaxPermission(chartResultPojo);
        initSqlHelper.tableJoin(groupSqlVOHashMapPair,chartEntity, isSingle, stringBuffer, aggFields, sortField, fliterMap, 100, 0,gropuFields.toString(),isMaxPermission,true);
        //分页
        Integer page = chartResultPojo.getPage() == null ? 1 : chartResultPojo.getPage();
        Integer pageSize = chartResultPojo.getPageSize() == null ? 20 : chartResultPojo.getPageSize();
        PageHelper pageHelper = new PageHelper(page, pageSize);
        generateSqlVO.setPageHelper(pageHelper);
        generateSqlVO.setStringBufferSql(stringBuffer);
        generateSqlVO.setAggFieldMap(groupSqlVO.getAggFieldMap());
        generateSqlVO.setSummaryAttrMap(summaryAttrMap);
        generateSqlVO.setSecondTargetKey(secondTargetKey);
        return generateSqlVO;
    }

    /**
     * 筛选条件在副指标图中的检验规则,当筛选的条件不是设置副指标的时间类型的时候就抛出异常，展示“筛选超过范围，请重新设置！”
     *
     * @param generateSqlDTO 描述sql参数信息
     * @param timeDimension  时间格式数据（y、y-m、y-w、y-m-d、y-q）
     * @return NormalTypeEnum 返回最后的操作时间范围
     */
    private NormalTypeEnum validSecondAdvancedRule(GenerateSqlDTO generateSqlDTO, String timeDimension) throws XbbException {
        ChartEntity chartEntity = generateSqlDTO.getChartEntity();
        JSONArray searchList = chartEntity.getSearch();
        // 用来过滤时间筛选条件，防止多个时间范围影响最终统计结果出错
        JSONArray newSearchList = new JSONArray();
        // 这两个枚举是为了验证当全局和非全局都存在的时候，他两是不是同一个
        NormalTypeEnum normalTypeEnum = null;
        TimeTypeEnum timeTypeEnum = null;
        if (CollectionsUtil.isEmpty(searchList)) {
            return NormalTypeEnum.UNKNOWN;
        }
        for (int i = 0; i < searchList.size(); i++) {
            JSONObject jsonObject = searchList.getJSONObject(i);
            List<Integer> timeIdList = FieldTypeEnum.esaliasTypeList().getTimeIdList();
            Integer fieldType = jsonObject.getInteger(FieldAttrConstant.FIELD_TYPE);
            if (!timeIdList.contains(fieldType)) {
                continue;
            }
            Integer global = jsonObject.getInteger(StringConstant.GLOBAL);
            if (Objects.equals(BasicConstant.ONE, global)) {
                // 全局筛选存在的时候
                ChartResultPojo chartResultPojo = generateSqlDTO.getChartResultPojo();
                if (Objects.isNull(chartResultPojo)) {
                    newSearchList.add(jsonObject);
                    continue;
                }
                FiscalYearTimePojo fiscalYearTimePojo = chartResultPojo.getFiscalYearTimePojo();
                if (Objects.isNull(fiscalYearTimePojo)) {
                    continue;
                }
                Integer timeType = fiscalYearTimePojo.getTimeType();
                if (Objects.isNull(timeType)) {
                    continue;
                }
                // 目前只支持常用筛选，不支持具体的月份、日、季度、年
                if (!Objects.equals(BasicConstant.ZERO, fiscalYearTimePojo.getDay())
                        || !Objects.equals(BasicConstant.ZERO, fiscalYearTimePojo.getMonth())
                        || !Objects.equals(BasicConstant.ZERO, fiscalYearTimePojo.getSeason())) {
                    throw new XbbException(ChartErrorCodeEnum.API_ERROR_1500019, ChartErrorCodeEnum.API_ERROR_1500019.getMsg());
                }
                timeTypeEnum = TimeTypeEnum.getByCode(timeType);
                List<TimeTypeEnum> timeTypeEnums = TimeTypeEnum.getByFormatStr(timeDimension);
                if (!timeTypeEnums.contains(timeTypeEnum)) {
                    throw new XbbException(ChartErrorCodeEnum.API_ERROR_1500019, ChartErrorCodeEnum.API_ERROR_1500019.getMsg());
                }
            } else {
                // 非全局存在的情况
                Integer normalType = jsonObject.getInteger(StringConstant.NORMAL_TYPE);
                if (Objects.isNull(normalType)) {
                    continue;
                }
                List<NormalTypeEnum> normalTypeEnums = NormalTypeEnum.getByFormatStr(timeDimension);
                normalTypeEnum = NormalTypeEnum.getByCode(normalType);
                if (!normalTypeEnums.contains(normalTypeEnum)) {
                    throw new XbbException(ChartErrorCodeEnum.API_ERROR_1500019, ChartErrorCodeEnum.API_ERROR_1500019.getMsg());
                }
            }
        }
        // 把非时间筛选字段作为筛选条件
        chartEntity.setSearch(newSearchList);

        // 如果有值需要作为范围筛选的话，就需要清除原来的时间范围,以免多个时间范围导致筛选出错
        if (Objects.isNull(normalTypeEnum) && Objects.isNull(timeTypeEnum)) {
            return null;
        } else if (Objects.isNull(normalTypeEnum)) {
            // 只存在全局的，需要转化为非全局的时间范围
            return TimeRangeOperationEnum.getNormalEnumByTimeType(timeTypeEnum);
        } else if (Objects.isNull(timeTypeEnum)) {
            // 只存在非全局的，正常返回即可
            return normalTypeEnum;
        } else {
            // 同时存在全局和非全局的时候必须是一样的范围才可以
            if (TimeRangeOperationEnum.isSameTime(normalTypeEnum, timeTypeEnum)) {
                return normalTypeEnum;
            } else {
                throw new XbbException(ChartErrorCodeEnum.API_ERROR_1500019, ChartErrorCodeEnum.API_ERROR_1500019.getMsg());
            }
        }
    }

    /**
     * 指标图表头拼接
     * @param chartEntity
     * @param isSingle
     * @param formIdTableMap
     * @param fieldAttrPojoList
     * @param tableHead
     * @param parseAttrMap
     * @throws XbbException
     * @author xinpeng.jiang
     * @date 2021/11/22 14:07
     * @version 1.0
     */
    private void handleHeadName4Target(ChartEntity chartEntity, boolean isSingle, Map<Long, String> formIdTableMap, List<ChartFieldAttrPojo> fieldAttrPojoList, JSONArray tableHead, Map<JSONObject, FieldAttrPojo> parseAttrMap) {
        if (CollectionUtils.isNotEmpty(fieldAttrPojoList)) {
            for (ChartFieldAttrPojo fieldAttrPojo : fieldAttrPojoList) {
                initSqlHelper.handleHeadName(chartEntity, isSingle, null, formIdTableMap, fieldAttrPojo, tableHead, parseAttrMap,null);
            }
        }
    }
}
